from sympy.geometry.plane import Plane
from sympy.geometry.point import Point, Point3D

# from sympy.geometry.polygon import Polygon3D, RegularPolygon
from sympy import S, sqrt
from sympy.core import Number
from sympy.core.symbol import symbols
from sympy.core.containers import Tuple
from sympy.solvers.solveset import nonlinsolve
from sympy.geometry.entity import GeometrySet
from sympy.geometry import Polygon, RegularPolygon

# from .polyhedron import Polyhedron
# from .utils import angle, points_to_3d, points_to_2d
# from .polygon3D import Polygon3D
from geometry3D.polyhedron import Polyhedron
from geometry3D.polygon3D import Polygon3D, RegularPolygon3D
from geometry3D.utils import angle, points_to_3d, points_to_2d

from .sphere import Sphere


class Pyramid(Polyhedron):
    """
    This is a class that use to build the pyramid.
    """

    def __new__(cls, vertice, polygon, *args, **kwargs):
        if vertice in S.Reals:
            if isinstance(polygon, RegularPolygon):
                vertice = Point(*polygon.center, vertice)
            else:
                raise ValueError("can't determin the vertise.")
        if not isinstance(polygon, (Polygon, Polygon3D)):
            raise ValueError("the bottom should be a polygon.")

        return super().__new__(
            cls, vertice, *points_to_3d(polygon.vertices), *args, **kwargs
        )

    @property
    def bottom(self) -> Polygon3D:
        return Polygon3D(*self.args[1::])

    @property
    def bottom_plane(self) -> Plane:
        return Plane(*self.bottom.vertices[0:3])

    @property
    def vertice(self) -> Point3D:
        return self.args[0]

    @property
    def height(self) -> Number:
        return Plane(self.bottom.vertices[0:3]).distance(self.vertice)

    @property
    def volume(self) -> Number:
        return self.bottom.area * self.height / S(3)

    @property
    def side_polygons(self) -> Tuple:
        sides = []
        vertice = self.vertice
        vertices = self.vertices
        bottom_points_number = len(vertices) - 1
        for i in range(1, bottom_points_number):
            sides.append(Polygon3D(vertice, vertices[i], vertices[i + 1]))
        sides.append(Polygon3D(vertice, vertices[-1], vertices[1]))
        return Tuple(*sides)

    @property
    def side_planes(self) -> Tuple:
        return map(Plane())


class Tetrahedron(Pyramid):

    @property
    def circumsphere(self):
        eqs = []
        a, b, c, r = symbols("a,b,c,r")
        x, y, z = symbols("x,y,z")
        eq = (x - a) ** 2 + (y - b) ** 2 + (z - c) ** 2 - r**2
        for point in self.vertices:
            eqs.append(eq.subs({x: point[0], y: point[1], z: point[2]}))
        x0, y0, z0, r2 = nonlinsolve(eqs, [a, b, c, r**2])
        return Sphere(Point(x0, y0, z0), sqrt(r2))
